package com.lipaluma.field.validator;

import static org.joox.JOOX.$;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;

import org.joox.Match;
import org.w3c.dom.Element;

import com.lipaluma.Xml;
import com.lipaluma.annotations.validations.Validate;
import com.lipaluma.annotations.validations.ValidateBoolean;
import com.lipaluma.annotations.validations.ValidateCollection;
import com.lipaluma.annotations.validations.ValidateDate;
import com.lipaluma.annotations.validations.ValidateDecimal;
import com.lipaluma.annotations.validations.ValidateInteger;
import com.lipaluma.exceptions.CsvParserException;

public class ValidationBasicOptions {
	private Class<? extends Validator>[] custom;
	private boolean required;

	private ValidationBasicOptions(Builder builder) {
		this.custom = builder.custom;
		this.required = builder.required;
	}
	
	public Class<? extends Validator>[] getCustomValidators() {
		return custom;
	}
	
	public boolean isRequired() {
		return required;
	}
	
	public static Builder builder() {
		return new Builder();
	}
	
	public static class Builder {
		private Class<? extends Validator>[] custom = new Class[]{};
		private boolean required = false;
		
		public Builder byAnnotation(Validate annotation) {
			if(annotation != null) {
				withCustomValidators(annotation.customs());
				required(annotation.required());
			}
			return this;
		}

		public Builder byAnnotation(ValidateBoolean annotation) {
			if(annotation != null) {
				withCustomValidators(annotation.customs());
				required(annotation.required());
			}
			return this;
		}

		public Builder byAnnotation(ValidateDate annotation) {
			if(annotation != null) {
				withCustomValidators(annotation.customs());
				required(annotation.required());
			}
			return this;
		}
		
		public Builder byAnnotation(ValidateDecimal annotation) {
			if(annotation != null) {
				withCustomValidators(annotation.customs());
				required(annotation.required());
			}
			return this;
		}

		public Builder byAnnotation(ValidateInteger annotation) {
			if(annotation != null) {
				withCustomValidators(annotation.customs());
				required(annotation.required());
			}
			return this;
		}

		public Builder byAnnotation(ValidateCollection annotation) {
			if(annotation != null) {
				withCustomValidators(annotation.customs());
			}
			return this;
		}

		public Builder byAnnotation(Annotation annotation) {
			if(annotation != null) {
				if(annotation instanceof Validate) {
					byAnnotation((Validate)annotation);
				}else if(annotation instanceof ValidateDate) {
					byAnnotation((ValidateDate)annotation);
				} else if(annotation instanceof ValidateInteger) {
					byAnnotation((ValidateInteger)annotation);
				} else if(annotation instanceof ValidateDecimal) {
					byAnnotation((ValidateDecimal)annotation);
				} else if(annotation instanceof ValidateBoolean) {
					byAnnotation((ValidateBoolean)annotation);
				}
			}
			return this;
		}

		public Builder byXml(Element element){
			List<Class<? extends Validator>> validators = new ArrayList<Class<? extends Validator>>();
			Match validatorElements = $(element).child(Xml.VALIDATORS_ELEMENT).children(Xml.VALIDATOR_ELEMENT);
			if(!validatorElements.isEmpty()) {
				for (Match validator : validatorElements.each()) {
					try {
						Class<?> clazz = Class.forName(validator.text());
						if(!Validator.class.isAssignableFrom(clazz))
							throw new CsvParserException("Class "+validator.text()+" is not a Validator class");
						validators.add((Class<?  extends Validator>)clazz);
					} catch (ClassNotFoundException e) {
						throw new CsvParserException("Class "+validator.text()+" as Validator is not found in classpath");
					} catch (ClassCastException e) {
					}
				}
			}
			this.custom = validators.toArray(new Class[0]);
			return this;
		}
		
		public Builder withCustomValidators(Class<? extends Validator>[] custom) {
			this.custom = custom;
			return this;
		}
		
		public Builder withCustomValidators(List<Class<? extends Validator>> custom) {
			this.custom = custom.toArray(new Class[0]);
			return this;
		}

		public Builder required(boolean required) {
			this.required = required;
			return this;
		}
		
		public ValidationBasicOptions build() {
			return new ValidationBasicOptions(this);
		}
	}

}
